package beautiful.butterfly.drds.datasource.connection_contexts.spi.thread;

//import beautiful.butterfly.drds.datasource.author.Author;
//import beautiful.butterfly.drds.datasource.author.Authors;

import beautiful.butterfly.drds.datasource.connection_contexts.spi.ConnectionContexts;
import beautiful.butterfly.drds.datasource.connection_contexts.spi.JdbcUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.SQLException;

//@Author(name = Authors.LI_YANG)
public class CreateConnectionContextThread extends Thread {
    public static final Logger logger = LoggerFactory.getLogger(CreateConnectionContextThread.class);

    private ConnectionContexts connectionContexts;

    public CreateConnectionContextThread(ConnectionContexts connectionContexts, String threadName) {
        super(threadName);
        this.connectionContexts = connectionContexts;
        this.setDaemon(true);
    }

    public void run() {
        this.connectionContexts.countDownLatch.countDown();

        long lastDiscardCount = 0;
        int errorCount = 0;
        for (; ; ) {
            // addLast
            try {
                this.connectionContexts.lock.lockInterruptibly();
            } catch (InterruptedException e2) {
                break;
            }

            long discardCount = this.connectionContexts.discardCount;
            boolean discardChanged = discardCount - lastDiscardCount > 0;
            lastDiscardCount = discardCount;

            try {
                boolean emptyWait = true;

                if (this.connectionContexts.exceptionWhenCreateConntection != null && this.connectionContexts.poolingIndex == 0 && !discardChanged) {
                    emptyWait = false;
                }

                if (emptyWait) {
                    // 必须存在线程等待，才创建连接
                    if (this.connectionContexts.poolingIndex >= this.connectionContexts.notEmptyWaitThreadCount //
                            && !(this.connectionContexts.keepAlive && this.connectionContexts.activeCount + this.connectionContexts.poolingIndex < this.connectionContexts.minIdle)) {
                        this.connectionContexts.empty.await();
                    }

                    // 防止创建超过maxActive数量的连接
                    if (this.connectionContexts.activeCount + this.connectionContexts.poolingIndex >= this.connectionContexts.maxActive) {
                        this.connectionContexts.empty.await();
                        continue;
                    }
                }

            } catch (InterruptedException e) {
                this.connectionContexts.lastCreateConntectionException = e;
                this.connectionContexts.lastErrorTimeMillis = System.currentTimeMillis();
                break;
            } finally {
                this.connectionContexts.lock.unlock();
            }

            Connection connection = null;

            try {
                connection = this.connectionContexts.createConnection();
                this.connectionContexts.failContinuous.set(false);
            } catch (SQLException e) {
                logger.error(e.getMessage());

                errorCount++;
                if (errorCount > this.connectionContexts.connectionErrorRetryAttempts && this.connectionContexts.timeMillisBetweenConnectException > 0) {
                    // fail over retry attempts
                    this.connectionContexts.failContinuous.set(true);
                    if (this.connectionContexts.failFast) {
                        this.connectionContexts.lock.lock();
                        try {
                            this.connectionContexts.notEmpty.signalAll();
                        } finally {
                            this.connectionContexts.lock.unlock();
                        }
                    }

                    if (this.connectionContexts.breakAfterAcquireFailure) {
                        break;
                    }

                    try {
                        Thread.sleep(this.connectionContexts.timeMillisBetweenConnectException);
                    } catch (InterruptedException interruptEx) {
                        break;
                    }
                }
            } catch (Error e) {
                logger.error("create connection error", e);
                this.connectionContexts.failContinuous.set(true);
                break;
            }

            if (connection == null) {
                continue;
            }

            boolean result = this.connectionContexts.put(connection);
            if (!result) {
                JdbcUtils.close(connection);
                logger.error("put physical connection to pool failed.");
            }

            errorCount = 0;
        }
    }
}
